#!/usr/bin/env bash

[ $UID -ne 0 ] && echo "This program has to be run as root!" && exit 1

FTP_NAME="ftp"
FTP_VUSERNAME="vsftpd"
PAM_CONF_DIR="/etc/pam.d"
FTP_CONF_DIR="/etc/vsftpd"
FTP_VCONF_DIR="/etc/vsftpd/vconf"
FTP_VROOT_DIR="/var/www"
FTP_ROOT_DIR="/var/$FTP_NAME"
FTP_VDB_FILE="$FTP_CONF_DIR/user_list.db"
ARCH=$(uname -m)
BACKUP_DATE=`date +%F-%H%M%S`

msg() {
    printf '%b\n' "$1" >&2
}

tips() {
    echo -e " \033[1;33m$@\033[0m";
}

die() {
    echo -e " \033[1;31m[Error]\033[0m  $@";  exit 1;
}

success() {
    [ "$?" -eq '0' ] && msg "\33[32m[OK]\33[0m ${1}${2}"
}

help_info() {
    cat << EOF
    usage: $0 [OPTIONS]

    help                                             show this information
    pre-init                                         install all depend tools
    init                                             configure vsftpd
    user-add <username> <passwd> <rw|r>              add user
    user-delete <username>                           delete user
    user-modify <username> [dir|passwd|mod] <value>  modify ftp user information
    user-list                                        list user
    get-passwd <username>                            get user password
    version                                          get vsftpd version
    start                                            service vsftpd start
    stop                                             service vsftpd stop
    restart                                          service vsftpd restart
EOF
}

detect_os() {
    if [[ -x $(command -v lsb_release 2>/dev/null) ]]; then
        DISTRO=`lsb_release -si`
        DISTMAJ=`lsb_release -r | awk '{print $2}' | cut -d. -f1`
    elif [ -f /etc/centos-release ]; then
        DISTRO=`cat /etc/centos-release | cut -d' ' -f1`
        DISTMAJ=`cat /etc/centos-release | sed "s/\(.*\)release//g" | cut -d. -f1 | sed "s/^ //g"`
    elif [ -f /etc/redhat-release ]; then
        DISTRO=`cat /etc/redhat-release | cut -d' ' -f1`
        DISTMAJ=`cat /etc/redhat-release | sed "s/\(.*\)release//g" | cut -d. -f1 | sed "s/^ //g"`
    # FIXME
    #elif [ -f /etc/debian_version ]; then
        #DISTRO="Debian"
    else
        die "Unable to determine DISTRO, can not continue."
    fi
}

get_tools() {
    if [[ "$DISTRO" =~ (Ubuntu) || "$DISTRO" =~ (Debian) ]]; then
        INS_TOOL=apt
    elif [[ "$DISTRO" =~ (CentOS) || "$DISTRO" =~ (Redhat) ]]; then
        INS_TOOL=yum
    fi
    which vsftpd > /dev/null 2>&1 && VSFTPD=`which vsftpd`
    which db_load > /dev/null 2>&1 && DB_LOAD=`which db_load`
    which db_dump > /dev/null 2>&1 && DB_DUMP=`which db_dump`
}

check_tools() {
    test -x "$VSFTPD" || die "Can not found vsftpd, run pre-init option please"
    test -x "$DB_DUMP" && test -x "$DB_LOAD" || die "Can not found db-util, run pre-init option please"
}

install_tools() {
    which vsftpd > /dev/null 2>&1 || ${INS_TOOL} install -y vsftpd
    which lftp > /dev/null 2>&1 || ${INS_TOOL} install -y lftp
    which ftp > /dev/null 2>&1 || ${INS_TOOL} install -y ftp
    which db_load > /dev/null 2>&1 || ${INS_TOOL} install -y libdb-utils
    which db_load > /dev/null 2>&1 || ${INS_TOOL} install -y db-util
    which db_load > /dev/null 2>&1 || ${INS_TOOL} install -y db4*
}

init() {
    [ ! -d $FTP_CONF_DIR ] && mkdir -p $FTP_CONF_DIR
    cp -r $FTP_CONF_DIR{,-$BACKUP_DATE.bak}
    rm -rf $FTP_CONF_DIR/*

    for i in `ls`
    do
        cp $i $FTP_CONF_DIR -r
    done

    mv $FTP_CONF_DIR/pam_vsftpd $PAM_CONF_DIR
    if [[ "$DISTRO" =~ (Ubuntu) || "$DISTRO" =~ (Debian) ]]; then
        ln -s $FTP_CONF_DIR/vsftpd.conf /etc
        sed -i 's:%LIB%:lib/x86_64-linux-gnu:' $PAM_CONF_DIR/pam_vsftpd
    elif [[ "$DISTRO" =~ (CentOS) || "$DISTRO" =~ (Redhat) ]]; then
        [[ $ARCH = 'x86_64' ]] &&
            sed -i 's:%LIB%:lib64:' $PAM_CONF_DIR/pam_vsftpd ||
            sed -i 's:%LIB%:lib:' $PAM_CONF_DIR/pam_vsftpd
    else
        die "Unable to determine plaform, can not continue."
    fi

    touch /var/log/vsftpd.log > /dev/null 2>&1
    touch $FTP_CONF_DIR/denyuser

    useradd $FTP_NAME -d $FTP_ROOT_DIR -s /sbin/nologin > /dev/null 2>&1
    useradd $FTP_VUSERNAME -d $FTP_VROOT_DIR -s /sbin/nologin > /dev/null 2>&1

    if [[ "$DISTRO" =~ (CentOS) ]]; then
        test -n "`cat /etc/profile | grep "setenforce 0"`" || echo "setenforce 0 > /dev/null 2>&1" >> /etc/profile
        source /etc/profile > /dev/null
    fi

    VERSION_MAJOR=`vsftpd -v 0>&1 | awk '{print $3}' | cut -d. -f1`
    if [[ $VERSION_MAJOR -gt 2 ]]; then
        echo "allow_writeable_chroot=YES" >> ${FTP_CONF_DIR}/vsftpd.conf
    fi
    tips "
    1. Configuration file located in /etc/vsftpd/
    2. pam_vsftpd file located in /etc/pam.d/pam_vsftpd
    3. virtual user root dir in ${FTP_VROOT_DIR}"
}

user_add() {
    local username=$1
    local passwd=$2
    local mode=$3

    [[ -z $username || -z $passwd || -z $mode ]] &&
        die "user_add: username or passwd or mode is null!"

    if [[ $mode == "rw" ]]; then
        cp $FTP_VCONF_DIR/templates/demo.r_w $FTP_VCONF_DIR/$username
    else
        cp $FTP_VCONF_DIR/templates/demo.r   $FTP_VCONF_DIR/$username
    fi
    echo "local_root=${FTP_VROOT_DIR}/${username}" >> $FTP_VCONF_DIR/$username
    mkdir -p $FTP_VROOT_DIR/$username
    chown -R $FTP_VUSERNAME:$FTP_VUSERNAME $FTP_VROOT_DIR/$username

    echo -e "$username\n$passwd" | $DB_LOAD -T -t hash $FTP_VDB_FILE
    echo "$username" >> ${FTP_CONF_DIR}/chroot_list
    chmod 600 $FTP_VDB_FILE
}

user_delete() {
    local username=$1

    [[ -z $username ]] && die "user-delete: username is null!"
    [ -f $FTP_VDB_FILE ] || die "user_list.db is not exits!"

    $DB_DUMP -p $FTP_VDB_FILE |
    sed -e '1,/HEADER=END/d' -e '/DATA=END/,$d' |
    sed 's/^[ \t]*//' |
    sed "/^$username$/{n;d}" |
    sed "/^$username$/d" |
    $DB_LOAD -T -t hash ${FTP_VDB_FILE}.new

    mv ${FTP_VDB_FILE}.new $FTP_VDB_FILE
    chmod 600 $FTP_VDB_FILE

    rm -rf $FTP_VCONF_DIR/$username
    mv $FTP_VROOT_DIR/$username{,-$BACKUP_DATE}
    success "$username already deleled"
    success "backed up to $FTP_VROOT_DIR/$username-$BACKUP_DATE."
}

user_modify() {
    local username=$1
    local option=$2
    local value=$3

    [[ -z $username || -z $option || -z $value ]] &&
        die "user_modify: username or option or value is null!"

    [ -f $FTP_VDB_FILE ] || die "user_list.db is not exits!"
    [ -f $FTP_VCONF_DIR/$username ] || die "user does not exits!"

    case $option in
        dir )
            cat $FTP_VCONF_DIR/$username | grep "write_enable=YES"
            if [[ $? -eq 0 ]]; then
                cp $FTP_VCONF_DIR/templates/demo.r_w $FTP_VCONF_DIR/$username
            else
                cp $FTP_VCONF_DIR/templates/demo.r   $FTP_VCONF_DIR/$username
            fi
            echo "local_root=${value}" >> $FTP_VCONF_DIR/$username
            mkdir -p $value
            chown -R $FTP_VUSERNAME:$FTP_VUSERNAME $value
            ;;
        passwd )
            echo -e "$username\n$value" | $DB_LOAD -T -t hash $FTP_VDB_FILE
            echo "$username" >> ${FTP_CONF_DIR}/chroot_list
            chmod 600 $FTP_VDB_FILE
            ;;
        mod )
            cat $FTP_VCONF_DIR/$username | grep -v "write_enable=" > $FTP_VCONF_DIR/${username}.new
            if [[ $value == "rw" ]]; then
                echo "write_enable=YES" >> $FTP_VCONF_DIR/${username}.new
            else
                echo "write_enable=NO" >> $FTP_VCONF_DIR/${username}.new
            fi
            mv $FTP_VCONF_DIR/${username}.new $FTP_VCONF_DIR/$username
            ;;
        *)
            echo "Unknown option: $2"
            help_info
            exit 1
            ;;
    esac
}

user_list() {
    [ -f $FTP_VDB_FILE ] || die "user_list.db is not exits!"
    $DB_DUMP -p $FTP_VDB_FILE |
    sed -e '1,/HEADER=END/d' -e '/DATA=END/,$d' |
    sed 's/^[ \t]*//' | sed -n 'p;n'
}

get_passwd() {
    local username=$1

    [[ -z $username ]] && die "get-passwd: username is null!"

    [ -f $FTP_VDB_FILE ] || die "user_list.db is not exits!"
    $DB_DUMP -p $FTP_VDB_FILE |
    sed -e '1,/HEADER=END/d' -e '/DATA=END/,$d' |
    sed 's/^[ \t]*//' | grep "^${username}$" -A1 | sed -n '2p'
}

get_version() {
    which vsftpd > /dev/null 2>&1 || die "cannot found vsftpd, please run init option"
    vsftpd -v 0>&1 | awk '{print $3}'
}

# Main
detect_os
get_tools

case $1 in
    --help|help|-h )
        help_info
        ;;
    pre-init )
        install_tools
        ;;
    init )
        check_tools
        init
        ;;
    user-add )
        check_tools
        user_add $2 $3 $4
        ;;
    user-delete )
        check_tools
        user_delete $2
        ;;
    user-modify )
        check_tools
        user_modify $2 $3 $4
        ;;
    user-list )
        check_tools
        user_list
        ;;
    get-passwd )
        check_tools
        get_passwd $2
        ;;
    --version|version|-v )
        check_tools
        get_version
        ;;
    start )
        service vsftpd start
        ;;
    stop )
        service vsftpd stop
        ;;
    restart )
        service vsftpd restart
        ;;
    *)
        echo "Unknown option: $1"
        help_info
        exit 1
        ;;
esac
